//DCPU 16 bit general purpose processor
//Implementing DCPU spec 1.7 by Notch
//16-bit word
//BLOCKS
//RAM 0x10000 words
//Registers: PC, SP, EX, A, B, C, X, Y, Z, I, J, IA
//Instruction decode/datafetch
//ALU Block
//Multiply & Divide Block
//Barrel Shifter
//Set Block
//HW Bus

module 2to1mux(sel, a, b, y);
input sel, a, b;
output y;
reg y;
always @(sel or a or b)
case (sel)
	0 : y = a;
	1 : y = b;
	default: $display("MUX ERROR");
endcase
endmodule

module 32to16mux(sel, a, b, y);
input sel, [15:0] a, [15:0] b;
output [15:0] y;
reg [15:0] y;
always @(sel or a or b)
case(sel)
	0 : y = a;
	1 : y = b;
	default $display("MUX ERROR");
endcase
endmodule

module 8to1mux16(sel, a, b, c, d, e, f, g, h, y);
	input [2:0] sel, [15:0] a, [15:0] b, [15:0] c, [15:0] d, [15:0] e, [15:0] f, [15:0] g, [15:0] h;
	output [15:0] y;
	always @(sel or a or b or c or d or e or f or g or h)
	case(sel)
		3'b000 : y = a;
		3'b001 : y = b;
		3'b010 : y = c;
		3'b011 : y = d;
		3'b100 : y = e;
		3'b101 : y = f;
		3'b110 : y = g;
		3'b111 : y = h;
		default $display("MUX ERROR");
		endcase
endmodule

module 3to8dec16(sel, a, r, s, t, u, v, x , y, z);
	input [2:0] sel, [15:0] a;
	output [15:0] r, [15:0] s, [15:0] t, [15:0] u, [15:0] v, [15:0] x, [15:0] y, [15:0] z;
	always @(sel or a or t or u or v or x or y or z)
	case (sel)
	3'b000 : begin t = a; u = 0; v = 0; x = 0; y = 0; z = 0; r = 0; s= 0; end
	3'b001 : begin t = 0; u = a; v = 0; x = 0; y = 0; z = 0; r = 0; s= 0; end
	3'b010 : begin t = 0; u = 0; v = a; x = 0; y = 0; z = 0; r = 0; s= 0; end
	3'b011 : begin t = 0; u = 0; v = 0; x = a; y = 0; z = 0; r = 0; s= 0; end
	3'b100 : begin t = 0; u = 0; v = 0; x = 0; y = a; z = 0; r = 0; s= 0; end
	3'b101 : begin t = 0; u = 0; v = 0; x = 0; y = 0; z = a; r = 0; s= 0; end
	3'b110 : begin t = 0; u = 0; v = 0; x = 0; y = 0; z = 0; r = a; s= 0; end
	3'b111 : begin t = 0; u = 0; v = 0; x = 0; y = 0; z = 0; r = 0; s= a; end
	default $display("MUX ERROR");
	endcase
endmodule

//Modeled using 7ALVC162834A
module 74ALV(clock, reset, le, oe, a, y);
input clock, reset, le, oe, [17:0] a;
output [17:0] y;
reg [17:0] latch;
//when LE is low, A to Y is transparent
//when LE is high, and CP is not transitioning, data is latched
//when LE is high, and CP is rising edge data is stored
//when OE is low, outputs are active
//when OE is high, outputs are high impedance (off)
always @(posedge clock) begin
	if(le)
		assign latch = a;
end
always @(posedge reset) begin
	assign latch = 18'b0;
end
if(oe)
	assign y = 18'b0;
else
	assign y = latch;
end
endmodule 

module reg_file16x8(clock, reset, wr, sel, a , y );
input clock, reset, wr, [2:0] sel, [15:0] a;
output [15:0] y;
wire [15:0] wa, [15:0] wb, [15:0] wc, [15:0] wx, [15:0] wy, [15:0] wz, [15:0] wi, [15:0] wj;
wire [15:0] ya, [15:0] yb, [15:0] yc, [15:0] yx, [15:0] yy, [15:0] yz, [15:0] yi, [15:0] yj;
3to8dec16(sel, a, wa, wb, wc, wx, wy, wz, wi, wj);
8to1mux16(sel, ya, yb, yc, yx, yy, yx, yi, yj, y);
//We want the le and oe signals controlled by wr
//When wr is 1, we are writing so drive le high
//When wr is 0, we are reading, so drive oe low
74ALV A(clock, reset, wr, wr, wa, ya);
74ALV B(clock, reset, wr, wr, wb, yb);
74ALV C(clock, reset, wr, wr, wc, yc);
74ALV X(clock, reset, wr, wr, wx, yx);
74ALV Y(clock, reset, wr, wr, wy, yy);
74ALV Z(clock, reset, wr, wr, wz, yz);
74ALV I(clock, reset, wr, wr, wi, yi);
74ALV J(clock, reset, wr, wr, wj, yj);
endmodule 

module toplevel();
reg [2:0] sel;
reg op;
reg clk;
reg rst;
reg [15:0] data;
reg [15:0] read;
reg_file16x8 rf(clk, rst, op, sel, data, read);
initial begin
	sel = 3'b000;
	data = 0;
	read = 0;
	op = 1;
	clk_reg = 0;
	rst = 1;
	#10 rst = 1;
	#10 rst = 0;
	#10 op = 1;
	#20 data = 16'hFFFF;
	#30 op = 0;
	#40 sel = 3'b001;
	#50 op = 1;
	#60 data = 16'hAAAA;
	#70 op = 0;
	#80 sel = 3'b010;
	#90 op = 1;
	#100 data = 16'hAEAE;
	#110 op = 0;
	#120 sel = 3'b011;
	#130 op = 1;
	#140 data = 16'h0101;
	#150 op = 0;
	#160 sel = 3'b100;
	#170 op = 1;
	#180 data = 16'h2525;
	#190 op = 0;
	#200 sel = 3'b101;
	#210 op = 1;
	#220 data = 16'hDFDF;
	#230 op = 0;
	#240 sel = 3'b110;
	#250 op = 1;
	#260 data = 16'h2C4C;
	#270 op = 0;
	#280 sel = 3'b111;
	#290 op = 1;
	#300 data = 16'h9D9D;
	#310 op = 0;
	#320 sel = 3'b000;
	#1 $display("Done");
	#1 $finish;
end

always begin
	#5 clk = !clk;
end



always @(sel or data or read)
	$display("@%0dns Sel:%b OP:%b Write:%h Read:&h\n", $time,sel, op, data, read);

	


 
endmodule 